/**
 * @Author: lena
 * @Description:
 * @Version: 1.0.0
 * @Date: 2021/8/27 22:24
 */

package datatype

import (
	"fmt"
	"time"
)

func Channel1() {
	// 创建channel方式一
	c1 := make(chan int)
	// 创建channel方式二:创建时指定容量
	c2 := make(chan string, 2)
	go func() {
		// 存入数据到channel中
		c1 <- 1001
		c2 <- "hello"
		c2 <- "world"
		c2 <- "lena"
	}()
	// 接收数据,同时flag用来判断通道是否关闭或是否为空
	b, flag := <-c1
	fmt.Println(flag, b) // true 1001
	// 接收数据
	x := <-c2
	// // 接收数据并丢弃:因为没有定义变量接收
	<-c2
	y := <-c2
	fmt.Println(x, y) // hello lena
}

func Channel2() {
	// 创建无缓冲channel
	c := make(chan int)
	fmt.Println("容量", cap(c))
	// 存入数据
	go func() {
		defer fmt.Println("go over")
		for i := 0; i < 3; i++ {
			c <- i
			fmt.Println("save", i, "len", len(c))
		}
	}()
	// 等待：看数据存入多少
	time.Sleep(1 * time.Second)
	// 取数据
	for i := 0; i < 3; i++ {
		res := <-c
		fmt.Println("get", res)
	}
	// 等待：确保协程执行完毕
	time.Sleep(2 * time.Second)
}

func Channel3() {
	// 创建容量为3的channel
	c := make(chan int, 3)
	// 存入数据
	go func() {
		defer fmt.Println("go over")
		for i := 0; i < 4; i++ {
			c <- i
			fmt.Println("save", i, "len", len(c))
		}
	}()
	// 等待：确保数据存入
	time.Sleep(1 * time.Second)
	// 取数据
	for i := 0; i < 4; i++ {
		res := <-c
		fmt.Println("get", res)
	}
	// 等待：确保协程执行完毕
	time.Sleep(1 * time.Second)
}

// 关闭管道
func Channel4() {
	c := make(chan int)
	go func() {
		// 存入数据
		for i := 0; i < 4; i++ {
			c <- i
		}
		// 关闭channel
		close(c)
	}()
	// 死循环：取出管道内数据
	for {
		// 只要channel还是打开状态,就会尝试获取数据
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			fmt.Println("nodata")
			break
		}
	}
	fmt.Println("over")
}

// 当参数类型一致的时候,可以不用都写
func fibonacii(data, quit chan int) {
	x, y := 1, 1
	// 死循环,直到quit有信号
	for {
		select {
		// 如果能向data内写入x,则会进入case内部
		case data <- x:
			x, y = x+y, x
		// 如果能从quit读到数据,则会进入case
		case <-quit:
			fmt.Println("quit") // 退出
			return              // 退出当前循环
		}
	}
}

// select
func Channel5() {
	data := make(chan int)
	quit := make(chan int) // 如果要退出,向管道存放数据
	// 取数据
	go func() {
		for i := 0; i < 10; i++ {
			// 从data中取数据 若无 则阻塞直到有数据
			fmt.Println(<-data)
		}
		// 相当于一个退出标志位:当退出取数据循环,表明已经不需要再存数据了
		quit <- 0
	}()
	// 存数据
	fibonacii(data, quit)
}

// 单向通道
func c1(ch1 chan<- int) {
	// 只能存值 不能取值
	for i := 0; i < 5; i++ {
		ch1 <- i
	}
	fmt.Println("add over")
	// 取值会报错
	//<- ch1
}

func c2(ch1 <-chan int) {
	// 只能取值 不能存值
	for i := 0; i < 5; i++ {
		fmt.Println("read", <-ch1)
	}
	fmt.Println("get over")
	// 存值会报错
	//ch1<-1
}

func Channel6() {
	ch := make(chan int, 5)
	go c1(ch)
	go c2(ch)
	for {
	}
}
